﻿using System;
using System.Collections.Generic;
using SCG.General;

namespace SCG.SolarVengeanceEngine
{
    //Represents a player in the game
    public class Player : SVObject
    {
        //constructor
        public Player(SVGame game)
            : base(game)
        {
            if (game != null)
            {
                game.Players.Add(this);
                Index = game.Players.Count - 1;            
            }
        }

        //The name of the player
        public string Name { get; set; }

        //Is it a human player?
        public bool IsHuman
        {
            get
            {
                return Brain == null;
            } 
        }

        //The Brain instance controlling this player
        public Brain Brain
        {
            get
            {
                return _brain;
            }
            set
            {
                _brain = value;
                if (value != null)
                {
                    Brain.Initialize(Game, this);

                    //assign available ship types
                    foreach (StarShipType st in Brain.AvailableShipTypes)
                        if (!ShipTypesAvailable.Contains(st))
                            ShipTypesAvailable.Add(st);

                    //assign name based on brain and instance count
                    string name = Brain.GetType().Name;
                    int n = 2;
                    while (Game.FindPlayer(name) != null)
                    {
                        name = Brain.GetType().Name + " " + n;
                        n++;
                    }
                    Name = name;
                }
            }
        }

        //allow the scenario to specify a desired Brain
        public string DesiredBrainName { get; set; }

        //Was the player a winner?
        public bool Victor { get; set; }

        //Their Capital StarSystem
        public StarSystem Capital
        {
            get
            {
                return _capital;
            }
            set
            {
                _capital = value;
                if (_capital != null)
                    _capital.Owner = this;
            }
        }

        //Player's color
        public SCGColor Color
        {
            get
            {
                if (_color == SCGColor.Transparent)
                {
                    int idx = 0;
                    bool colorAlreadyUsed;
                    do
                    {
                        colorAlreadyUsed = false;
                        if (idx == BasicColors.Length)
                        {
                            _color = SCGColor.RandomColor;
                            while (_color.Luminance < 60)
                                _color.Brighten();
                        }
                        else
                        {
                            _color = BasicColors[idx];
                            idx++;
                        }

                        foreach (Player other in Game.Players)
                            if (other != this)
                                if (other.Color.SimilarTo(_color))
                                {
                                    colorAlreadyUsed = true;
                                    break;
                                }
                    }
                    while (colorAlreadyUsed);
                }
                return _color;
            }
            set
            {
                //allow setting only if another player does not have the color
                bool alreadyUsed = false;
                foreach (Player other in Game.Players)
                    if (other != this)
                        if (other._color == value)
                        {
                            alreadyUsed = true;
                            break;
                        }
                if (!alreadyUsed)
                    _color = value;
            }
        }

        //Access their glyph image
        public object Glyph { get; set; }

        //The StarShips owned by this player
        public List<StarShip> StarShips
        {
            get
            {
                return _starShips;
            }
        }

        //An index into the player list
        public int Index
        {
            get
            {
                return _index;
            }
            private set
            {
                _index = value;
            }
        }

        //Tech multiple
        public double TechMultiple
        {
            get
            {
                return _techMultiple;
            }
            set
            {
                _techMultiple = value;
            }
        }

        //have they dropped from a multiplayer game?
        public bool DroppedFromGame { get; set; }

        //Get alliance level toward another player
        public AllianceLevel AlliedWith(Player p)
        {
            if (p == null)
                return AllianceLevel.None;
            if (_alliances.ContainsKey(p))
                return _alliances[p];
            return AllianceLevel.None;
        }
        public void SetAlliance(Player p, AllianceLevel alliance)
        {
            _alliances[p] = alliance;

            //alert the host
            if (Game.Host != null)
                Game.Host.AllianceChanged(this, p);
        }

        //Friendly to another player?
        public bool FriendlyTo(Player p)
        {
            if (p == this)
                return true;
            return AlliedWith(p) > AllianceLevel.None;
        }

        //Was the player eliminated from the game?
        public bool Eliminated
        {
            get
            {
                return _eliminated;
            }
            set
            {
                if (value != _eliminated)
                {
                    _eliminated = value;

                    //notify host
                    if (value)
                        if (Game.Host != null)
                            Game.Host.PlayerEliminated(this);
                }
            }
        }

        //Did they surrender?
        public bool Surrendered { get; set; }

        //Ships that the Player can build
        public List<StarShipType> ShipTypesAvailable
        {
            get
            {
                return _shipTypesAvailable;
            }
        }

        //Execute Brain orders
        public List<string> ExecuteBrain()
        {
            try
            {
                return Brain.InitializeAndExecute(Game, this, Game.Host);
            }
            catch
            {
                //swallow Brain errors
                return new List<string>();
            }
        }

        //number of starsystems owned
        public int NumStarSystems
        {
            get
            {
                int n = 0;
                foreach (StarSystem sys in Game.StarSystems)
                    if (sys.Owner == this)
                        n++;
                return n;
            }
        }

        //Persistence      
        public string Persist()
        {
            SVTokenizer tok = new SVTokenizer(Game);
            _capitalID = Capital == null ? -1 : Capital.ID;
            tok.Tokenize(Name, _capitalID, TechMultiple, Eliminated, Color);
            tok.AppendToken(ShipTypesAvailable.Count);
            foreach (StarShipType st in ShipTypesAvailable)
                tok.AppendToken(st);
            tok.AppendToken(_alliances.Count);
            foreach (KeyValuePair<Player, AllianceLevel> kvp in _alliances)
            {
                tok.AppendToken(kvp.Key);
                tok.AppendToken(kvp.Value);
            }
            if (Brain != null)
            {
                tok.AppendToken(Brain.GetType().Name);
                tok.AppendToken(Brain.GetBrainData());
            }
            else
                tok.AppendToken("");
            return tok.Result;
        }
        public void Parse(string s, ISVGameReconstituter reconstituter)
        {
            SVTokenizer tok = new SVTokenizer(Game, s);
            Name = tok.TokenString;
            _capitalID = tok.TokenInt;
            TechMultiple = tok.TokenDouble;       
            Eliminated = tok.TokenBoolean;
            Color = tok.TokenColor;
            int n = tok.TokenInt;
            while (n > 0)
            {
                ShipTypesAvailable.Add(tok.TokenStarShipType);
                n--;
            }
            n = tok.TokenInt;
            while (n > 0)
            {
                Player ally = tok.TokenPlayer;
                AllianceLevel al = tok.TokenAllianceLevel;
                _alliances[ally] = al;
                n--;
            }
            string brainName = tok.TokenString;
            if (brainName != "")
            {
                Brain = reconstituter.GetBrain(brainName);
                Brain.ParseBrainData(tok.TokenString);            
            }
        }
        public void Relink()
        {
            if (_capitalID >= 0)
                Capital = Game.LookupMapObject(_capitalID) as StarSystem;
        }

        //for debugging
        public override string ToString()
        {
            return Name;
        }

        //Basic colors available
        public static SCGColor[] BasicColors = { SCGColor.Red, SCGColor.Yellow, new SCGColor(255, 0, 255, 255), 
            SCGColor.Orange, new SCGColor(255, 0, 255, 0), new SCGColor(255, 255, 0, 255),
            new SCGColor(255, 128, 128, 0), new SCGColor(255, 0, 128, 128), new SCGColor(255, 0, 128, 0) };        

        //private members
        private StarSystem _capital = null;
        private int _capitalID = -1;
        private List<StarShip> _starShips = new List<StarShip>();
        private int _index;
        private double _techMultiple = 1.0d;
        private List<StarShipType> _shipTypesAvailable = new List<StarShipType>();
        private SCGColor _color = SCGColor.Transparent;
        private Dictionary<Player, AllianceLevel> _alliances = new Dictionary<Player, AllianceLevel>();
        private Brain _brain = null;
        private bool _eliminated = false;
    }
}
